package com.sharp.authorization.service.docking.service;

import com.sharp.authorization.constants.common.RequestConst;
import com.sharp.authorization.dao.common.repository.AcUserService;
import com.sharp.authorization.service.docking.dto.SignDTO;
import com.sharp.authorization.service.docking.exception.TokenInvalidException;
import com.sharp.authorization.service.docking.utils.DockingSignUtil;
import com.sharp.kernel.config.response.Response;
import com.sharp.kernel.constant.err.ErrCodeSys;
import com.sharp.kernel.exception.ServiceException;
import com.sharp.framework.service.redis.RedisService;
import com.sharp.framework.service.rest.RestService;
import com.sharp.kernel.utils.JsonUtil;
import com.sharp.framework.vo.token.TokenVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * Title: BaseDockingService
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2021/6/10 15:26
 */
@Slf4j
@Service
public abstract class BaseDockingService {
    @Resource
    protected RedisService redisService;
    @Resource
    protected RestService restService;
    @Resource
    private AcUserService acUserService;

    protected abstract String getAccessTokenKey();

    protected abstract int getAccessTokenExpired();

    protected abstract String getBaseUrl();

    protected abstract String getAppId();

    public synchronized String getAccessToken() {
        String accessToken = redisService.get(getAccessTokenKey(), String.class);
        if (StringUtils.isEmpty(accessToken)) {
            TokenVO res = fetchAccessToken();
            redisService.set(getAccessTokenKey(), getAccessTokenExpired(), res.getToken());
            accessToken = res.getToken();
        }
        return accessToken;
    }

    public TokenVO fetchAccessToken() {
        SignDTO signDTO = buildSignDTO();
        Response res = restService.get(getBaseUrl() + "/token", signDTO, Response.class);
        return JsonUtil.readJson(res.getData(), TokenVO.class);
    }

    @Retryable(value = {TokenInvalidException.class}, maxAttempts = 5)
    public <T> T post(String url, Class<T> resClazz, Object req) {
        return post(url, getHttpHeaders(), resClazz, req);
    }

    @Retryable(value = {TokenInvalidException.class}, maxAttempts = 5)
    public <T> T post(String url, HttpHeaders header, Class<T> resClazz, Object req) {
        if (header == null) {
            header = getHttpHeaders();
        }
        Response<?> res = restService.post(url, header, Response.class, req);
        handleCommonError(res);
        return JsonUtil.readJson(res.getData(), resClazz);
    }

    @Retryable(value = {TokenInvalidException.class}, maxAttempts = 5)
    public <T> T get(String url, Object param, Class<T> resClazz) {
        return get(url, getHttpHeaders(), param, resClazz);
    }

    @Retryable(value = {TokenInvalidException.class}, maxAttempts = 5)
    public <T> T get(String url, HttpHeaders header, Object param, Class<T> resClazz) {
        if (header == null) {
            header = getHttpHeaders();
        }
        Response<?> res = restService.get(url, header, param, Response.class);
        handleCommonError(res);
        return JsonUtil.readJson(res.getData(), resClazz);
    }

    private void handleCommonError(Response<?> res) {
        if (res == null) {
            throw new ServiceException("访问失败");
        } else if (StringUtils.equals(res.getMessage(), ErrCodeSys.SYS_SESSION_TIME_OUT.name())) {
            redisService.delete(getAccessTokenKey());
            throw new TokenInvalidException();
        } else if (!StringUtils.equals(res.getCode(), ErrCodeSys.SUCCESS.getErrCode())) {
            log.info("系统错误: {}-{}", res.getMessage(), res.getData());
        }
    }

    private SignDTO buildSignDTO() {
        String secret = acUserService.getUserByAppKey(getAppId()).getAppSecret();
        SignDTO signDTO = DockingSignUtil.sign(secret);
        return signDTO;
    }

    private HttpHeaders getHttpHeaders() {
        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);
        header.set(RequestConst.TOKEN, getAccessToken());
        header.set(RequestConst.CLIENT_TYPE, "WEB");
        return header;
    }
}
